

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "../mac.h"

#include "core_mqtt.h"
#include "core_mqtt_state.h"
#include "network_transport.h"
#include "backoff_algorithm.h"

#include "clock.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

#include "aws_iot_connect.h"
#include "aws_iot_Publish.h"
#include "aws_iot_Subscribe.h"



#ifndef CLIENT_CERTIFICATE_PEM
extern const char client_cert_pem_start[] asm("_binary_client_crt_start");
extern const char client_cert_pem_end[] asm("_binary_client_crt_end");
#endif
#ifndef CLIENT_PRIVATE_KEY_PEM
extern const char client_key_pem_start[] asm("_binary_client_key_start");
extern const char client_key_pem_end[] asm("_binary_client_key_end");
#endif

//AWS IoT 指定的 MQTT字符串，用于建立连接
#define METRICS_STRING "?SDK=" OS_NAME "&Version=" OS_VERSION "&Platform=" HARDWARE_PLATFORM_NAME "&MQTTLib=" MQTT_LIB
#define METRICS_STRING_LENGTH ((uint16_t)(sizeof(METRICS_STRING) - 1))


#define AWS_IOT_ENDPOINT_LENGTH ((uint16_t)(sizeof(AWS_IOT_ENDPOINT) - 1))

static const char *TAG = "AWS IoT客户端";
uint8_t haveMqttSession = 0;


//在已连接的TSL,发送MQTT连接包,请求建立MQTT连接会话
static int establishMqttSession(MQTTContext_t *pMqttContext, bool createCleanSession, bool *pSessionPresent)
{
    int returnStatus = EXIT_SUCCESS;
    MQTTStatus_t mqttStatus;
    MQTTConnectInfo_t connectInfo = {0};

    /* If #createCleanSession is true, start with a clean session
     * i.e. direct the MQTT broker to discard any previous session data.
     * If #createCleanSession is false, directs the broker to attempt to
     * reestablish a session which was already present. */
    connectInfo.cleanSession = createCleanSession;
    /* The client identifier is used to uniquely identify this MQTT client to
     * the MQTT broker. In a production device the identifier can be something
     * unique, such as a device serial number. */
    connectInfo.pClientIdentifier = PublishTopicName();
    connectInfo.clientIdentifierLength = publishName_Length;
    /* The maximum time interval in seconds which is allowed to elapse
     * between two Control Packets.
     * It is the responsibility of the Client to ensure that the interval between
     * Control Packets being sent does not exceed the this Keep Alive value. In the
     * absence of sending any other Control Packets, the Client MUST send a
     * PINGREQ Packet. */
    connectInfo.keepAliveSeconds = 60;
    /* Use the username and password for authentication, if they are defined.
     * Refer to the AWS IoT documentation below for details regarding client
     * authentication with a username and password.
     * https://docs.aws.amazon.com/iot/latest/developerguide/custom-authentication.html
     * An authorizer setup needs to be done, as mentioned in the above link, to use
     * username/password based client authentication.
     *
     * The username field is populated with voluntary metrics to AWS IoT.
     * The metrics collected by AWS IoT are the operating system, the operating
     * system's version, the hardware platform, and the MQTT Client library
     * information. These metrics help AWS IoT improve security and provide
     * better technical support.
     *
     * If client authentication is based on username/password in AWS IoT,
     * the metrics string is appended to the username to support both client
     * authentication and metrics collection. */
    connectInfo.pUserName = METRICS_STRING;
    connectInfo.userNameLength = METRICS_STRING_LENGTH;
    //不使用用于身份验证的密码。
    connectInfo.pPassword = NULL;
    connectInfo.passwordLength = 0U;

    ESP_LOGE(TAG,"通过发送 CONNECT 数据包, 跟AWS IoT建立 MQTT会话");
    mqttStatus = MQTT_Connect(pMqttContext, &connectInfo, NULL, 1000, pSessionPresent);
    if (mqttStatus != MQTTSuccess)
    {
        returnStatus = EXIT_FAILURE;
        ESP_LOGE(TAG,"MQTT会话, 建立失败, 原因:%s.", MQTT_Status_strerror(mqttStatus));
    }
    else
    {
        haveMqttSession = 1;
        ESP_LOGE(TAG,"MQTT会话, 建立成功");
        SubscribeLoop(pMqttContext);
    }

    return returnStatus;
}
static void disconnectMqttSession(MQTTContext_t *pMqttContext)
{
    MQTTStatus_t mqttStatus = MQTTSuccess;

    if(haveMqttSession == 0) return;
    haveMqttSession = 0;

    ESP_LOGE(TAG,"断开Mqtt会话");
    mqttStatus = MQTT_Disconnect(pMqttContext);
    if (mqttStatus != MQTTSuccess)
    {
        ESP_LOGE(TAG,"发送MQTT会话断开 失败, 原因=%s.", MQTT_Status_strerror(mqttStatus));
    }
    else
    {
        ESP_LOGE(TAG,"MQTT会话断开 成功");
    }
}


//通过回退重试连接到服务器
void connectToAWS(MQTTContext_t *pMqttContext, NetworkContext_t *pNetworkContext)
{
    uint16_t nextRetryBackOff;
    int returnStatus = EXIT_SUCCESS;
    BackoffAlgorithmStatus_t backoffAlgStatus = BackoffAlgorithmSuccess;
    TlsTransportStatus_t tlsStatus = TLS_TRANSPORT_SUCCESS;
    BackoffAlgorithmContext_t reconnectParams;
    bool brokerSessionPresent = false;

    pNetworkContext->pcHostname = "a2bfkvo4me6plu-ats.iot.us-west-2.amazonaws.com";
    pNetworkContext->xPort = 8883;
    pNetworkContext->pxTls = NULL;
    pNetworkContext->xTlsContextSemaphore = xSemaphoreCreateMutex();
    pNetworkContext->disableSni = 0;
    //初始化用于建立 TLS 会话的凭据
    pNetworkContext->pcServerRootCAPem = root_cert_auth_pem_start;
    pNetworkContext->pcClientCertPem = client_cert_pem_start;
    pNetworkContext->pcClientKeyPem = client_key_pem_start;
    pNetworkContext->pAlpnProtos = NULL;

    ESP_LOGE(TAG,"通过TLS 连接到 AWS IoT");
    tlsStatus = xTlsConnect(pNetworkContext);
    if (tlsStatus != TLS_TRANSPORT_SUCCESS)
    {
        ESP_LOGE(TAG,"连接到 AWS IoT,连接失败...");
        returnStatus = EXIT_FAILURE;
    }
    else
    {
        ESP_LOGE(TAG,"连接到 AWS IoT,连接成功. 消耗近40k内存");
        
        //建立 MQTT连接
        //更新标志以指示已保存 MQTT 客户机会话。设置此标志后，将尝试在此演示的以下迭代中进行 MQTT 连接，而无需请求干净的会话。
        establishMqttSession(pMqttContext, false, &brokerSessionPresent);
        //检查会话是否存在，以及是否有任何需要重新发送的传出发布。仅当代理正在重新建立已存在的会话时，这才有效。
        if (brokerSessionPresent == true)
        {
            ESP_LOGE(TAG,"与代理重新建立 MQTT会话. 重新发送未添加的发布.");
            // 处理发布消息的所有重新发送.
            returnStatus = handlePublishResend(pNetworkContext);
        }
        else
        {
            // ESP_LOGE(TAG,"建立干净的 MQTT会话, 清理所有存储的传出发布...\n\n");
            // 清理等待 ack 的传出发布，因为此新连接不会重新建立现有会话.
            cleanupOutgoingPublishes();
        } 
    }
    
    return returnStatus;
}

void disconnectAWS(MQTTContext_t *pMqttContext, NetworkContext_t *pNetworkContext)
{
    disconnectMqttSession(pMqttContext);
    ESP_LOGE(TAG,"断开AWS IoT连接");
    int t = xTlsDisconnect(pNetworkContext); //释放分配的使用内存,防止使用内存无限递增
    if(t == TLS_TRANSPORT_SUCCESS) ESP_LOGE(TAG,"断开AWS IoT连接...成功");
    else ESP_LOGE(TAG,"断开AWS IoT连接...失败,原因(%d)",t);
    vPortFree(pNetworkContext->xTlsContextSemaphore); //释放FreeRTOS分配的内存,防止内存溢出
    pNetworkContext->xTlsContextSemaphore = NULL;
}
void AWS_Disconnect(void)
{
    // ESP_LOGE(TAG,"取消订阅主题。 %.*s.", MQTT_EXAMPLE_TOPIC_LENGTH, MQTT_EXAMPLE_TOPIC);
    // returnStatus = unsubscribeFromTopic(pMqttContext);
    // ESP_LOGE(TAG,"处理来自代理的传入 UNSUBACK 数据包。");
    // mqttStatus = MQTT_ProcessLoop(pMqttContext, 100);
    // if (mqttStatus != MQTTSuccess)
    // {
    //     returnStatus = EXIT_FAILURE;
    //     ESP_LOGE(TAG,"MQTT_ProcessLoop 返回状态 = %s.", MQTT_Status_strerror(mqttStatus));
    // }
    
    // 通过已连接的 TCP 套接字发送 MQTT 断开连接数据包。断开连接数据包没有相应的响应。发送断开连接后，客户端必须关闭网络连接。
    // if (mqttSessionEstablished == true)
    // {
    //     ESP_LOGE(TAG,"断开 MQTT 连接 %.*s.", AWS_IOT_ENDPOINT_LENGTH, AWS_IOT_ENDPOINT);
    //     if (returnStatus == EXIT_FAILURE)
    //     {
    //         /* 返回的状态不用于更新本地状态，因为演示执行失败。 */
    //         disconnectMqttSession(pMqttContext);
    //     }
    //     else
    //     {
    //         returnStatus = disconnectMqttSession(pMqttContext);
    //     }
    // }
    // ESP_LOGE(TAG,"在订阅请求周期完成后重置全局 SUBACK 状态变量。");
    // globalSubAckStatus = MQTTSubAckFailure;
}

